package com.norman.sorry.core.boards;

import java.awt.Color;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import com.norman.sorry.core.moves.IMove;
import com.norman.sorry.core.moves.InvalidMoveException;
import com.norman.sorry.core.pieces.IPiece;
import com.norman.sorry.core.tiles.ILinkedSlide;
import com.norman.sorry.core.tiles.ILinkedTile;

/**
 * A helper class for creating a concrete version of a game board.
 * 
 * @author Brian Norman
 * @version 0.0.1 beta
 * @since 0.0.1 beta
 */
public abstract class AbstractBoard implements IBoard {

   /**
    * The starting tiles of all the different colors.
    */
   protected Map<Color, ILinkedTile>  starts;

   /**
    * The mapping of all the game pieces to their respective tile.
    */
   protected Map<IPiece, ILinkedTile> pieces;

   /**
    * Creates a new game board with the given game pieces and starting tiles.
    * The game board will automatically place the game pieces at their
    * respective starting tiles.
    * 
    * @param pieces
    *           the game pieces on this game board.
    * @param startMap
    *           the starting tiles of this game board.
    */
   public AbstractBoard(Collection<IPiece> pieces, Map<Color, ILinkedTile> startMap) {
      this.starts = startMap;
      this.pieces = new HashMap<IPiece, ILinkedTile>() {
         private static final long serialVersionUID = 8257550839085540190L;

         @Override
         public ILinkedTile put(IPiece key, ILinkedTile value) {
            if (key == null) {
               return null;
            } else {
               return super.put(key, value);
            }
         }
      };

      for (IPiece p : pieces) {
         this.pieces.put(p, starts.get(p.getColor()));
      }
   }

   /**
    * {@inheritDoc}
    */
   @Override
   public boolean isValid(IMove move) {
      for (IPiece p : move.getPieces()) {
         ILinkedTile tile = move.getTile(p);

         if (tile instanceof ILinkedSlide && ((ILinkedSlide) tile).canSlide(p.getColor())
               && ((ILinkedSlide) tile).containsPiece()) {
            // can slide and other pieces on the slide
            for (ILinkedTile t : ((ILinkedSlide) tile).getSlide()) {
               if (t.getPiece() != null && t.getPiece().getColor().equals(p.getColor())) {
                  return false;
               }
            }
         } else if (tile.getPiece() != null && tile.getPiece().getColor().equals(p.getColor())) {
            // tile has another piece
            return false;
         }
      }
      return true;
   }

   /**
    * {@inheritDoc}
    * 
    * @throws InvalidMoveException
    */
   @Override
   public void move(IMove move) {
      if (!isValid(move)) {
         throw new InvalidMoveException("Move is not valid.");
      }

      for (IPiece piece : move.getPieces()) {
         ILinkedTile tile = move.getTile(piece);

         // remove pieces
         if (tile instanceof ILinkedSlide && ((ILinkedSlide) tile).canSlide(piece.getColor())) {
            ILinkedSlide slide = (ILinkedSlide) tile;
            for (IPiece p : slide.removePieces()) {
               pieces.put(p, starts.get(p.getColor()));
            }

            // change new position to end of slide
            tile = slide.getEnd();
         } else if (tile.getPiece() != null) {
            pieces.put(tile.getPiece(), starts.get(tile.getPiece().getColor()));
         }

         // put piece in map and remove from old
         pieces.put(piece, tile).setPiece(null);
         tile.setPiece(piece);
      }
   }

}
